package com.wjw.cloud.common.util;

import org.apache.commons.lang.StringUtils;
import org.apache.http.*;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.*;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.nio.charset.CodingErrorAction;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author yujisun
 */
public class HttpClientUtil
{

    private static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);

    private static CloseableHttpClient httpClient = null;

    private static RequestConfig requestConfig = null;

    static
    {
        try
        {
            SSLContext sslContext = SSLContexts.custom().useTLS().build();
            sslContext.init(null,
                    new TrustManager[]{new X509TrustManager()
                    {
                        public X509Certificate[] getAcceptedIssuers()
                        {
                            return null;
                        }

                        public void checkClientTrusted(X509Certificate[] certs, String authType)
                        {
                        }

                        public void checkServerTrusted(X509Certificate[] certs, String authType)
                        {
                        }
                    }}, null);

            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("https", new SSLConnectionSocketFactory(sslContext))
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .build();
            // Create socket configuration
            SocketConfig socketConfig = SocketConfig.custom().setTcpNoDelay(true).build();

            // Create message constraints
            MessageConstraints messageConstraints = MessageConstraints.custom()
                    .setMaxHeaderCount(200)
                    .setMaxLineLength(2000)
                    .build();

            // Create connection configuration
            ConnectionConfig connectionConfig = ConnectionConfig.custom()
                    .setMalformedInputAction(CodingErrorAction.IGNORE)
                    .setUnmappableInputAction(CodingErrorAction.IGNORE)
                    .setCharset(Consts.UTF_8)
                    .setMessageConstraints(messageConstraints)
                    .build();

            //请求重试处理
            HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler()
            {
                public boolean retryRequest(IOException exception, int executionCount, HttpContext context)
                {
                    if (executionCount >= 3)
                    {// 如果已经重试了3次，就放弃
                        return false;
                    }
                    if (exception instanceof NoHttpResponseException)
                    {// 如果服务器丢掉了连接，那么就重试
                        return true;
                    }
                    if (exception instanceof SSLHandshakeException)
                    {// 不要重试SSL握手异常
                        return false;
                    }
                    if (exception instanceof ConnectTimeoutException)
                    {// 连接被拒绝
                        return false;
                    }
                    if (exception instanceof InterruptedIOException)
                    {// 超时
                        return false;
                    }
                    if (exception instanceof UnknownHostException)
                    {// 目标服务器不可达
                        return false;
                    }
                    if (exception instanceof SSLException)
                    {// ssl握手异常
                        return false;
                    }

                    HttpClientContext clientContext = HttpClientContext.adapt(context);
                    HttpRequest request = clientContext.getRequest();
                    // 如果请求是幂等的，就再次尝试
                    return !(request instanceof HttpEntityEnclosingRequest);
                }
            };

            PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            connManager.setDefaultSocketConfig(socketConfig);
            connManager.setDefaultConnectionConfig(connectionConfig);
            connManager.setMaxTotal(200);
            connManager.setDefaultMaxPerRoute(200);
            httpClient = HttpClients.custom()
                    .setConnectionManager(connManager)
                    .setRetryHandler(httpRequestRetryHandler)
                    .build();

            requestConfig = RequestConfig.custom()
                    .setConnectionRequestTimeout(30000)
                    .setConnectTimeout(30000)
                    .setSocketTimeout(30000).build();
        }
        catch (NoSuchAlgorithmException e)
        {
            e.printStackTrace();
        }
        catch (KeyManagementException e)
        {
            e.printStackTrace();
        }
    }

    public static String xmlPOST(String url, String jsonString) throws Exception
    {
        logger.info("url={} jsonString={}",url,jsonString);
        HttpPost postRequest = new HttpPost(url);
        try
        {
            StringEntity input = new StringEntity(jsonString,"UTF-8");
            input.setContentType("application/xml");

            input.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/xml"));
            postRequest.setEntity(input);
            postRequest.setHeader("Accept", "application/xml");
            postRequest.setHeader("Content-Type", "application/xml");

            postRequest.setConfig(requestConfig);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            throw e;
        }

        //返回
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        try
        {
            response = httpClient.execute(postRequest);
            entity = response.getEntity();
            if (entity != null)
            {
                return unicode2String(EntityUtils.toString(entity, "UTF-8"));
            }
            else
            {
                return null;
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
            throw e;
        }
        finally
        {
            try
            {
                if (entity != null)
                {
                    entity.getContent().close();
                }
                if (response != null)
                {
                    response.close();
                }
                postRequest.releaseConnection();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }


    public static String xmlPOSTReturnJson(String url, String jsonString) throws Exception
    {
    	logger.info("jsonString={}",jsonString);
        HttpPost postRequest = new HttpPost(url);
        try
        {
            StringEntity input = new StringEntity(jsonString,"UTF-8");
            input.setContentType("application/xml");

            input.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/xml"));
            postRequest.setEntity(input);
            postRequest.setHeader("Accept", "application/json");
            postRequest.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");

            postRequest.setConfig(requestConfig);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            throw e;
        }

        //返回
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        try
        {
            response = httpClient.execute(postRequest);
            entity = response.getEntity();
            if (entity != null)
            {
                return unicode2String(EntityUtils.toString(entity, "UTF-8"));
            }
            else
            {
                return null;
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
            throw e;
        }
        finally
        {
            try
            {

                if (entity != null)
                {
                    entity.getContent().close();
                }
                if (response != null)
                {
                    response.close();
                }
                postRequest.releaseConnection();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }

    public static String jsonPost(String url, String jsonString) throws Exception
    {
        logger.info("jsonString={} url={}",jsonString,url);
        HttpPost postRequest = new HttpPost(url);
        try
        {
            StringEntity input = new StringEntity(jsonString,"UTF-8");
            input.setContentType("application/json");
            input.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
            postRequest.setEntity(input);
            postRequest.setHeader("Accept", "application/json");
            postRequest.setHeader("Content-Type", "application/json");

            postRequest.setConfig(requestConfig);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            throw e;
        }

        //返回
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        try
        {
            response = httpClient.execute(postRequest);
            entity = response.getEntity();
            if (entity != null)
            {
                return unicode2String(EntityUtils.toString(entity, "UTF-8"));
            }
            else
            {
                return null;
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
            throw e;
        }
        finally
        {
            try
            {

                if (entity != null)
                {
                    entity.getContent().close();
                }
                if (response != null)
                {
                    response.close();
                }
                postRequest.releaseConnection();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }

    public static String doPost(String url, Map<String, String> headers, String jsonString) throws Exception
    {
        logger.info("jsonString={}",jsonString);
        HttpPost postRequest = new HttpPost(url);
        try
        {
            for (Map.Entry<String, String> e : headers.entrySet()) {
                postRequest.addHeader(e.getKey(), e.getValue());
            }

            if (StringUtils.isNotBlank(jsonString)) {
                postRequest.setEntity(new StringEntity(jsonString, "utf-8"));
            }
            postRequest.setConfig(requestConfig);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            throw e;
        }

        //返回
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        try
        {
            response = httpClient.execute(postRequest);
            entity = response.getEntity();
            if (entity != null)
            {
                return unicode2String(EntityUtils.toString(entity, "UTF-8"));
            }
            else
            {
                return null;
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
            throw e;
        }
        finally
        {
            try
            {

                if (entity != null)
                {
                    entity.getContent().close();
                }
                if (response != null)
                {
                    response.close();
                }
                postRequest.releaseConnection();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }

    public static String jsonPost(String url, List<NameValuePair> pairs) throws Exception
    {
        HttpPost httpPost = new HttpPost(url);
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        try
        {
            httpPost.setEntity( new UrlEncodedFormEntity(pairs,"utf-8"));
            httpPost.setConfig(requestConfig);
            response = httpClient.execute(httpPost);
            entity = response.getEntity();
            String jsonStr = EntityUtils.toString( entity, "utf-8");
            return jsonStr;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            throw e;
        }
        finally
        {
            try
            {
                httpPost.releaseConnection();
                if (entity != null)
                {
                    entity.getContent().close();
                }
                if (response != null)
                {
                    response.close();
                }
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }

    public static String xmlPOSTReturnJson(String url, Map<String,String> paramMap) throws Exception
    {
        logger.info("paramMap={}",paramMap);
        HttpPost postRequest = new HttpPost(url);
        try
        {
            if (paramMap != null) {
                List<NameValuePair> paramList = new ArrayList<NameValuePair>();
                for (Map.Entry<String, String> param : paramMap.entrySet()) {
                    NameValuePair pair = new BasicNameValuePair(param.getKey(), param.getValue());
                    paramList.add(pair);
                }
                postRequest.setEntity(new UrlEncodedFormEntity(paramList, "UTF-8"));
            }
            postRequest.setHeader("Accept", "application/json");
            postRequest.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");

            postRequest.setConfig(requestConfig);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            throw e;
        }

        //返回
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        try
        {
            response = httpClient.execute(postRequest);
            entity = response.getEntity();
            if (entity != null)
            {
                return unicode2String(EntityUtils.toString(entity, "UTF-8"));
            }
            else
            {
                return null;
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
            throw e;
        }
        finally
        {
            try
            {

                if (entity != null)
                {
                    entity.getContent().close();
                }
                if (response != null)
                {
                    response.close();
                }
                postRequest.releaseConnection();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }

    private static String unicode2String(String theString)
    {
        char aChar;
        int len = theString.length();
        StringBuilder outBuffer = new StringBuilder(len);

        for (int x = 0; x < len; )
        {
            aChar = theString.charAt(x++);
            if (aChar == '\\')
            {
                aChar = theString.charAt(x++);
                if (aChar == 'u')
                {
                    int value = 0;
                    for (int i = 0; i < 4; i++)
                    {
                        aChar = theString.charAt(x++);
                        switch (aChar)
                        {
                            case '0':
                            case '1':
                            case '2':
                            case '3':
                            case '4':
                            case '5':
                            case '6':
                            case '7':
                            case '8':
                            case '9':
                                value = (value << 4) + aChar - '0';
                                break;
                            case 'a':
                            case 'b':
                            case 'c':
                            case 'd':
                            case 'e':
                            case 'f':
                                value = (value << 4) + 10 + aChar - 'a';
                                break;
                            case 'A':
                            case 'B':
                            case 'C':
                            case 'D':
                            case 'E':
                            case 'F':
                                value = (value << 4) + 10 + aChar - 'A';
                                break;
                            default:
                                throw new IllegalArgumentException("Malformed   \\uxxxx   encoding.");
                        }
                    }
                    outBuffer.append((char) value);
                }
                else
                {
                    if (aChar == 't')
                    {
                        aChar = '\t';
                    }
                    else if (aChar == 'r')
                    {
                        aChar = '\r';
                    }
                    else if (aChar == 'n')
                    {
                        aChar = '\n';
                    }
                    else if (aChar == 'f')
                    {
                        aChar = '\f';
                    }
                    outBuffer.append(aChar);
                }
            }
            else
            {
                outBuffer.append(aChar);
            }
        }
        return outBuffer.toString();
    }

    public static void main(String[] s)
    {
        String url = "http://10.252.29.244:8080/api-1.0-SNAPSHOT/user/login.htm";
        String jsonString = "{\"merchantUserId\":\"张三10\",\"merchantId\":1}";
        try
        {
            HttpClientUtil.xmlPOSTReturnJson(url, jsonString);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}
